#ifndef GX_FR_ENGINE_MATERIAL_H
#define GX_FR_ENGINE_MATERIAL_H

#include "Basic/GxLibBasic.h"
#include "LibGL/GxMath3D.h"
#include "Serialization.h"
#include "Regex/MetaLexer.h"
#include "LibGL/GxTextures.h"
#include "Shader.h"

namespace FinalRendering
{
	namespace Engine
	{
		using namespace GxRendering;

		class MaterialFile : public Object
		{
		private:
			static RefPtr<GxMetaParser::MetaLexer> lexer;
		public:
			class Uniform
			{
			public:
				String Name;
				String Value;
				union
				{
					float FloatVal;
					int IntVal;
				};
				vec3 VecVal;
				enum UniformType
				{
					Float, Vec3, Int,
					Texture
				} Type;
			};
		public:
			String ShaderName;
			String FileName;
			Array<RefPtr<Uniform>> Uniforms;
			MaterialFile();
			MaterialFile(const String & FileName);
			Uniform * FindUniform(const String & name);
			void Serialize(StringBuilder & sb);
			bool UnSerialize(TokenNode * token);
			void Load(const String & FileName);
			void Save(const String & FileName);
			static void Init();
			static void Finalize();
		};

		class ProgramPool : public Object
		{
		private:
			Array<String> progNames;
			Array<RefPtr<Program>> programs;
		public:
			Program * LoadProgram(const String & shaderName);
		};

		class Material : public Object
		{
		private:
			vec3 DiffuseRate;
			class Uniform
			{
			public:
				enum UniformType
				{
					Int, Float, Vec3
				} Type;
				int Location;
				union
				{
					float FloatVal;
					int IntVal;
					struct
					{
						float VecX, VecY, VecZ;
					};
				};
			};
			String shaderName;
			Array<Uniform> Uniforms;
			Program * program;
			Array<RefPtr<Texture>> textures;
		public:
			typedef Event<Material *, Object *, const String &, int> SetClientUniformEvent;

			SetClientUniformEvent OnSetClientUniform;

			String GetShaderName();
			int GetTextureCount();
			vec3 GetDiffuseRate();
			Program * GetProgram();
			Texture * GetDiffuseTexture();
			Material(const MaterialFile & matFile, ProgramPool * program);
			void Bind(Object * targetObj = 0, bool switchProgram = true);
		};
	}
}

#endif